/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#include "ak_obj.h"
#include "ak_ecast.h"
#include "ak_gears.h"

/* The Cursor of the Event : Used in the Cast */
#define _SEvent_CursorGet(pEvent) 		((pEvent)->cursor)
#define _SEvent_CursorSet(pEvent, vCursor) \
do{\
	_SEvent_CursorGet(pEvent) = (unsigned int)(vCursor); \
}while(0)

/* The CasId of the Event : Used in the Cast */
#define _SEvent_CasIdGet(pEvent) 		((pEvent)->casId)
#define _SEvent_CasIdSet(pEvent, vCasId) \
do{\
	_SEvent_CasIdGet(pEvent) = (unsigned int)(vCasId); \
}while(0)


static 	int 	_gTraceLvl_SEvtMc = TRACELVL_NORMAL;

int 	SEvtMc_SetTraceLvl(int lvl)
{
	int 	ret = -1;
	
	switch(lvl){
		case TRACELVL_FATAL:
		case TRACELVL_ERROR:
		case TRACELVL_WARNING:
		case TRACELVL_NORMAL:
		case TRACELVL_DEBUG:
		case TRACELVL_ALL:
			_gTraceLvl_SEvtMc = lvl;
			ret = 0;
			break;
		default:
			break;
	}

	return ret;
}

int 	SEvtMc_GetTraceLvl(void)
{
	return  _gTraceLvl_SEvtMc;
}


static 	__inline int 	_SEventCast_Fini(_t_sEvent * pEvent)
{
	unsigned int 	mType; 	
	_t_sEvtPool * 	pEvtPool;
	
	if(pEvent){
		/* Check the MType */
		mType = _SEvent_MType(pEvent);
		switch(mType){
			case _sIE_MT_STATIC:
				_SEvent_Init(pEvent, 0, 0, 0, 0);
				break;
				
			case _sIE_MT_EX:
				pEvtPool = pEvent->ie.cont.msg.pool;
				if(pEvtPool){
					pEvtPool->evtFree(pEvtPool, pEvent);
				}
				else{
					SYS_TRACE(_gTraceLvl_SEvtMc, TRACELVL_ERROR, "Event(%p): Invalid Pool.\n", 
								pEvent); 
				}
				break;
				
			default:
				SYS_TRACE(_gTraceLvl_SEvtMc, TRACELVL_ERROR, "Event(%p): Invalid MType(%u)\n", 
							pEvent, mType); 
				break;
				
		}
	}
	return -1;
}

/* User API:
** Object Cast Event. The Object ID is Used as Key to Add Different Object to the MC
**
*/
int 	SEvtMc_Init(_t_sEvtMC * pEvtMC, _t_sEnv * pEnv, _t_cbentry * eProc[_SEvent_Pri_Max])
{
	int 			i, ret;
	unsigned int 	batch;
	_t_sEvtCast * 	pEvtCast;
	
	ret = -1;
	if(pEvtMC && pEnv){
		/* Enviorment */
		pEvtMC->pEnv 	= pEnv;

		for(i=0; i<_SEvent_Pri_Max; i++){
			pEvtCast = &(pEvtMC->pri[i]);
			pEvtCast->wake	= pEnv->wake;
			
			/* Init the Cast */
			BLIST_INIT(&(pEvtCast->que));
			SYS_INIT_CS(&(pEvtCast->lock));

			/* Calc the Batch */
			switch(i){
				case _SEvent_Pri_Critical:
				case _SEvent_Pri_High:
					/* Calc the Batch */
					batch = 1;
					break;
			
				case _SEvent_Pri_Medium:
					/* Calc the Batch */
					batch = 2;
					break;
					
				case _SEvent_Pri_Low:
				default:
					/* Calc the Batch */
					batch = 4;
					break;
			}
			_CBENTRY_ASSIGN(&(pEvtCast->eProc), eProc[i]);
			pEvtCast->eStat= 0;
			
		}

		
		/* Init the Cast Table */
		SHTblCa_Init(&(pEvtMC->caTbl));

		ret = 0;
	}
	return ret;
}


int 	SEvtMc_ObjAdd(_t_sEvtMC * pEvtMC, _t_sObjSlot * pObjSlot)
{
	int 				ret;
	_t_sObject 	* 		pObj;

	ret = -1;
	if(pEvtMC && pObjSlot && (pObj = pObjSlot->pObj) && (pObj->id != 0)){
		ret = SHTblCa_TblAdd(&(pEvtMC->caTbl), 
							(int *)(&(pObj->id)), 
							sizeof(unsigned int)/sizeof(unsigned char), 
							&(pObjSlot->node));
	}

	return ret;
}


int 	SEvtMc_ObjDel(_t_sEvtMC * pEvtMC, _t_sObjSlot * pObjSlot)
{
	int 				ret;
	_t_sObject 	* 		pObj;

	ret = -1;
	if(pEvtMC && pObjSlot && (pObj = pObjSlot->pObj) && (pObj->id != 0)){
		ret = SHTblCa_TblDel(&(pEvtMC->caTbl), 
							(int *)(&(pObj->id)), 
							sizeof(unsigned int)/sizeof(unsigned char), 
							&(pObjSlot->node));
	}

	return ret;
}

static 	int 	_SEvtEnt_Sche(_t_hTblCa_Ent * pEnt, _t_cbentry * eProc, _t_sEvent * pEvent)
{
	_t_sObjSlot * 	pObjSlot;
	_t_sObject 	* 	pObject;
	_t_bnode	  * 	pNode;
	_t_bnode	  * 	pNext;
	_t_blist	  * 	pQue;
	int 		 	i, count;

	pQue = &(pEnt->eQue);
	count= pQue->count;
	for(i=0, pNode=pQue->head; i<count; i++, pNode=pNext){
		pNext 		= pNode->next;
		pObjSlot	= _O_PARENT(_t_sObjSlot, node, pNode);
		pObject 	= pObjSlot->pObj;
		if(pObject && pObject->evtHdl){
			pObject->evtHdl(pObject, pEvent, NULL);
		}
		else{
			/* Default Process */
			eProc->cbProc(eProc->pArg, eProc->lArg, pEvent);
		}
	}

	return 0;
}


static 	int 	_SEvtHTbl_Sche(_t_hTblCaS * pCaS, _t_cbentry * eProc, _t_sEvent * pEvent, unsigned int batch)
{
	_t_hTblCa_Ent * pEnt;
	int 		 	i;
	unsigned int 	cursor;
	
	if(pCaS && pEvent){
		/* Calc the Real Batch */
		if(batch == 0){
			batch = 1;
		}
		if(batch > _cHTBL_BUCKET_MAX){
			batch = _cHTBL_BUCKET_MAX;
		}
		batch = (_cHTBL_BUCKET_MAX)/batch;

		/* Calc the Cursor */
		cursor = _SEvent_CursorGet(pEvent);
		if(cursor >= _cHTBL_BUCKET_MAX){
			SYS_TRACE(_gTraceLvl_SEvtMc, TRACELVL_ERROR, "Proc(%p): Cursor -> %-4u \n", 
						pEvent, cursor); 
			return -1;
		}
		if((batch + cursor) >= _cHTBL_BUCKET_MAX){
			batch = _cHTBL_BUCKET_MAX - cursor;
		}

		/* Proc */
		pEnt = pCaS->buckets + cursor;
		for(i=0; i<batch; i++, pEnt++, cursor++){
			SYS_LOCK_WR_TAKE(&(pEnt->lock));
			_SEvtEnt_Sche(pEnt, eProc, pEvent);
			SYS_LOCK_RW_GIVE(&(pEnt->lock));
		}
		/* ReNew the Cursor */
		_SEvent_CursorSet(pEvent, cursor);
		
		if(cursor == _cHTBL_BUCKET_MAX){
			return -1;
		}
	}

	return 0;
}

static 	int 	_SEvtCast_Sche(_t_sEvtCast * pEvtCast, _t_hTblCaS * pCaS)
{
	long 				i, count;
	unsigned int 		batch;
	_t_cbentry 	  *		eProc;
	_t_sEvent 	  * 	pEvent;
	_t_sysMi 	  * 	pMi;
	_t_bnode 	  * 	pNode;
	_t_bnode 	  * 	pNext;
	_t_blist 	  * 	pQue;
	
	count = 0;
	batch = pEvtCast->batch;
	eProc = &(pEvtCast->eProc);
	if((batch > 0) && (!_SEvtCast_IsEmpty(pEvtCast))){
		count = _SEvtCast_Count(pEvtCast);
		SYS_TRACE(_gTraceLvl_SEvtMc, TRACELVL_DEBUG, "Sche(%p): %-4d -> %-4u \n", 
					pEvtCast, count, batch); 
		
		if(count > batch){
			count = batch;
		}
		
		/* Add Statistics */
		if(pEvtCast->eStat < count){
			pEvtCast->eStat = count;
		}
		
		/* Retrive the JobNode */
		SYS_ENTER_CS(&(pEvtCast->lock));
		pQue = &(pEvtCast->que);
		for(i=0, pNode=pQue->head; i<count; i++, pNode=pNext){
			pNext 	= pNode->next;
			pMi  	= _O_PARENT(_t_sysMi, aNode, pNode);
			pEvent  = (_t_sEvent *)(pMi + 1);

			/* Checking the Object */
			if(_SEvtHTbl_Sche(pCaS, eProc, pEvent, batch) == -1){
				/* Finish Proc & Free */
				BLST_DEL(pQue, pNode);
				_SEventCast_Fini(pEvent);
			}
			else{
				/* Here we Bubble It :( */
				BLST_BUBBLE2T(pQue, pNode);
			}
		
		}
		SYS_LEAVE_CS(&(pEvtCast->lock));
		
	}
	
	return count;
}

int 	SEvtMc_Sche(_t_sEvtMC * pEvtMC)
{
	_t_hTblCaS  * pCaS;
	_t_sEvtCast * pEvtCast;
	int 		i, j, count;

	count = 0;
	if(pEvtMC){
		pCaS = &(pEvtMC->caTbl);
		for(i=0; i<_SEvent_Pri_Max; i++){
			/* We Check the Critical - High - Medium - Low */
			for(j=0; j<=i; j++){
				pEvtCast = &(pEvtMC->pri[j]);
				count = _SEvtCast_Sche(pEvtCast, pCaS);
			}
		}
	}
	return count;
}

int 	SEvtMc_Cancel(_t_sEvtMC * pEvtMC, unsigned int priority, unsigned int type, unsigned int casId)
{
	_t_sEvtCast * pEvtCast;
	long 		i, count;
	_t_bnode 	  * 	pNode;
	_t_bnode 	  * 	pNext;
	_t_sysMi 	  * 	pMi;
	_t_blist 	  * 	pQue;
	_t_sEvent 	  * 	pEvent;

	if(pEvtMC && (priority < _SEvent_Pri_Max)){
		pEvtCast = pEvtMC->pri + priority;
		count = _SEvtCast_Count(pEvtCast);

		SYS_ENTER_CS(&(pEvtCast->lock));
		pQue = &(pEvtCast->que);
		for(i=0, pNode=pQue->head; i<count; i++, pNode=pNext){
			pNext 	= pNode->next;
			pMi   	= _O_PARENT(_t_sysMi, aNode, pNode);
			pEvent	= (_t_sEvent *)(pMi + 1);

			/* Checking the Object */
			if(	(_SEvent_GetType(pEvent) == type) &&
				(_SEvent_GetPri(pEvent)	 == priority) &&
				(_SEvent_CasIdGet(pEvent)== casId)){
				/* Finish Proc & Free */
				BLST_DEL(pQue, pNode);
				_SEventCast_Fini(pEvent);
				break;
			}
		}
		SYS_LEAVE_CS(&(pEvtCast->lock));
		return 0;
	}

	return -1;
}



/* Event Group Cast */
static 	int 	_SEvtMc_Post(_t_sEvtMC * pEvtMC, _t_sEvent * pEvent)
{
	_t_sEvtCast * 	pCast;
	_t_sysMi 	* 	pMi;
	int 			i, ret;

	pCast = pEvtMC->pri + _SEvent_GetPri(pEvent);	/* Priority Ring */
	i = 0;

	pMi = (_t_sysMi *)(pEvent - 1);
	/* Here We Check the Event's IE's mType: */
	SOP_OP_WAIT(_SEvtCast_EnQue(pCast, pMi, ret), 
				ret,
				i,
				_SEvtPost_Retry,
				SOP_WAIT_FAST);
	
	if(ret == -1){
		SYS_TRACE(_gTraceLvl_SEvtMc, TRACELVL_ERROR, "EvtMC(%p): Post(%p) Error\n", 
					pEvtMC, pEvent); 
		_SEventCast_Fini(pEvent);
	}

	return ret;
}


int 	SEvtMc_PostMsg(_t_sEvtMC * pEvtMC, _t_sEvent * pIn, void * pMsg, unsigned int lMsg)
{
	_t_sEvtPool	*	pEvtPool;
	_t_sEvent 	* 	pEvent;
	unsigned int 	pri;
	int 			ret;

	ret = -1;
	if(	pEvtMC && (pEvtMC->pEnv) && (pEvtPool = (pEvtMC->pEnv->pEvtPool)) && 
		pIn 	&&(pri = _SEvent_GetPri(pIn)) && (pri < _SEvent_Pri_Max)){
		if(pMsg && (lMsg > 0)){
			/* Must be Alloc Ex */
			if((pEvent = pEvtPool->evtAlloc(pEvtPool, (lMsg + _SEVT_EX_EV)))){
				_SEvent_Init(pEvent, 0, 0, 0, 0);
				_SEvent_SetParam(pEvent, pIn);
				/* Init the Cont */
				_SCont_SetMsg(&(pEvent->ie.cont), _sIE_MT_EX, ((unsigned char *)(pEvent + 1)), 
					lMsg, pEvtPool);
				/* Construct the Msg */
				SYS_MEMCPY(pEvent->ie.cont.msg.ptr, pMsg, lMsg);
				_SEvent_MsgLen(pEvent) = lMsg;
			}else{
				SYS_TRACE(_gTraceLvl_SEvtMc, TRACELVL_ERROR, "Event(%p): MC(%p) Alloc Error\n", 
							pIn, pEvtMC); 
				return -1;
			}
		}
		else{
			_SIE_SetMType(&(pIn->ie), _sIE_MT_STATIC);
		}
		ret =  _SEvtMc_Post(pEvtMC, pIn);
	}
	else{
		SYS_TRACE(_gTraceLvl_SEvtMc, TRACELVL_ERROR, "Event(%p): MC(%p) Param Error\n", 
					pIn, pEvtMC); 
	}
	return ret;
}



